package handler

import (
	"dealcenter/internal/cache"
	"dealcenter/internal/discovery"
	"dealcenter/internal/model"
	"dealcenter/internal/queue"
	"dealcenter/internal/service"
	"dealcenter/utils/logx"
	"dealcenter/utils/resx"
	"dealcenter/utils/secret"
	"github.com/gin-gonic/gin"
	"strconv"
	"time"
)

func SaleList(ctx *gin.Context) {
	jwt := ctx.Request.Header.Get("Authorization")
	claims, err := secret.GetJwtClaims(jwt)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
	}
	var res []model.OrderSale
	ok, lastPage := Paging("sale", &res, claims, ctx)
	if ok != true {
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, &resx.DataList{
		Items:    res,
		Total:    len(res),
		LastPage: lastPage,
	}))
	return
}
func BuyList(ctx *gin.Context) {
	jwt := ctx.Request.Header.Get("Authorization")
	claims, err := secret.GetJwtClaims(jwt)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}
	var res []model.OrderBuy
	ok, lastPage := Paging("buy", &res, claims, ctx)
	if ok != true {
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, &resx.DataList{
		Items:    res,
		Total:    len(res),
		LastPage: lastPage,
	}))
	return
}
func DealList(ctx *gin.Context) {
	jwt := ctx.Request.Header.Get("Authorization")
	claims, err := secret.GetJwtClaims(jwt)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}
	var res []model.Deal
	ok, lastPage := Paging("deal", &res, claims, ctx)
	if ok != true {
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, &resx.DataList{
		Items:    res,
		Total:    len(res),
		LastPage: lastPage,
	}))
	return
}

func Paging(modelType string, items interface{}, claims *secret.CtpClaims, ctx *gin.Context) (ok bool, lastPage int64) {
	var mt interface{}
	switch modelType {
	case "sale":
		mt = model.OrderSale{}
	case "buy":
		mt = model.OrderBuy{}
	case "deal":
		mt = model.Deal{}
	}

	stat := ctx.DefaultQuery("stat", "ready")
	part := ctx.DefaultQuery("part", "1")
	if part == "0" {
		if claims.Audience[0] == "sys" {
			model.QueryItems(stat, part, 0, 0, items)
			return true, -1
		}
		switch modelType {
		case "sale":
			model.QuerySalesByUserId(stat, part, 0, 0, items, claims.UserId)
		case "buy":
			model.QueryBuysByUserId(stat, part, 0, 0, items, claims.UserId)
		case "deal":
			model.QueryDealsByUserId(stat, part, 0, 0, items, claims.UserId)
		}
		return true, -1
	}

	page := ctx.DefaultQuery("page", "1")
	pmax := ctx.DefaultQuery("pmax", "6")

	pageInt, err1 := strconv.Atoi(page)
	pmaxInt, err2 := strconv.Atoi(pmax)
	if err1 != nil || err2 != nil {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return false, 0
	}
	offset := (pageInt - 1) * pmaxInt
	count, err := model.QueryItemCount(stat, &mt)

	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return false, 0
	}
	if count <= int64(offset) {
		if count == 0 && pageInt == 1 {
			ctx.JSON(200, resx.GinResp(resx.Success, &resx.DataList{Items: nil, Total: 0, LastPage: 1}))
			return false, 0
		}
		ctx.JSON(200, resx.GinResp(resx.OutOfRange, &gin.H{"last": count/int64(pmaxInt) + 1}))
		return false, 0
	}

	if claims.Audience[0] == "sys" {
		model.QueryItems(stat, part, offset, pmaxInt, items)
		return true, count/int64(pmaxInt) + 1
	}
	switch modelType {
	case "sale":
		model.QuerySalesByUserId(stat, part, offset, pmaxInt, items, claims.UserId)
	case "buy":
		model.QueryBuysByUserId(stat, part, offset, pmaxInt, items, claims.UserId)
	case "deal":
		model.QueryDealsByUserId(stat, part, offset, pmaxInt, items, claims.UserId)
	}
	return true, count/int64(pmaxInt) + 1
}

func SaleDetail(ctx *gin.Context) {
	jwt := ctx.Request.Header.Get("Authorization")
	claims, err := secret.GetJwtClaims(jwt)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	saleId := ctx.Param("id")
	if saleId == "" {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}

	if claims.Audience[0] == "sys" {
		claims.UserId = -1
	}

	res := model.QuerySaleByUserIdOrNot(saleId, claims.UserId)
	if res.State == "" {
		ctx.JSON(200, resx.GinResp(resx.NilResult, nil))
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, res))
	return
}
func BuyDetail(ctx *gin.Context) {
	jwt := ctx.Request.Header.Get("Authorization")
	claims, err := secret.GetJwtClaims(jwt)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	buyId := ctx.Param("id")
	if buyId == "" {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}

	if claims.Audience[0] == "sys" {
		claims.UserId = -1
	}

	res := model.QueryBuyByUserIdOrNot(buyId, claims.UserId)
	if res.State == "" {
		ctx.JSON(200, resx.GinResp(resx.NilResult, nil))
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, res))
	return
}
func DealDetail(ctx *gin.Context) {
	jwt := ctx.Request.Header.Get("Authorization")
	claims, err := secret.GetJwtClaims(jwt)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	dealId := ctx.Param("id")
	if dealId == "" {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}

	if claims.Audience[0] == "sys" {
		claims.UserId = -1
	}

	res := model.QueryDealByUserIdOrNot(dealId, claims.UserId)
	if res.State == "" {
		ctx.JSON(200, resx.GinResp(resx.NilResult, nil))
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, res))
	return
}

func SaleCreate(ctx *gin.Context) {
	jwt := ctx.Request.Header.Get("Authorization")
	claims, err := secret.GetJwtClaims(jwt)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	req := SaleCreateRequest{}
	if err = ctx.BindJSON(&req); err != nil {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}

	// 预生成订单
	leftNum, dealList := model.QueryMixedBuy(req.GoodId, req.MinPrice, req.Num, claims.UserId)

	// 定时关闭
	for _, dealId := range dealList {
		pubErr := queue.PubItemClose("deal", queue.RkWait1D, dealId)
		if pubErr != nil {
			logx.LogrusObj.Warning("[API] Publish close failed, reason:", pubErr)
		}
	}

	// 预生成后再生成订单，避免多次访问数据库
	var timestamp int64
	switch req.WaitType {
	case queue.RkWait1M:
		timestamp = 60 * 1000
	case queue.RkWait1H:
		timestamp = 60 * 60 * 1000
	case queue.RkWait1D:
		timestamp = 24 * 60 * 60 * 1000
	default:
		req.WaitType = queue.RkWait1M
		timestamp = 60 * 1000
	}

	createState := "ready"
	if leftNum == 0 {
		createState = "closed"
	}
	sale := model.OrderSale{
		GoodId:   req.GoodId,
		SalerId:  claims.UserId,
		MinPrice: req.MinPrice,
		Num:      req.Num,
		LeftNum:  leftNum,
		Exp:      time.Now().UnixMilli() + timestamp,
		State:    createState,
	}
	err = model.InsertSale(&sale)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	if leftNum != 0 {
		pubErr := queue.PubItemClose("sale", req.WaitType, sale.SaleId)
		if pubErr != nil {
			logx.LogrusObj.Warning("[API] Publish close failed, reason:", pubErr)
		}
	}

	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

func SaleClose(ctx *gin.Context) {
	jwt := ctx.Request.Header.Get("Authorization")
	claims, err := secret.GetJwtClaims(jwt)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	saleId := ctx.Param("id")

	err = model.UpdateSaleByUserIdAndSaleId(claims.UserId, saleId, "closed")
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.Fail, nil))
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

func BuyCreate(ctx *gin.Context) {
	jwt := ctx.Request.Header.Get("Authorization")
	claims, err := secret.GetJwtClaims(jwt)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	req := BuyCreateRequest{}
	if err = ctx.BindJSON(&req); err != nil {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}

	// 预扣款
	cashConn := discovery.CashDiscover.Get()
	cashServiceClient := service.NewCashServiceClient(cashConn)
	rpcResp, rpcErr := cashServiceClient.CashDec(ctx, &service.CashChangeRequest{
		Count:  req.MaxPrice * req.Num,
		UserId: claims.UserId,
	})
	if rpcResp.GetCode() == 14 {
		discovery.CashDiscover.Sad(cashConn.Target())
	}
	if rpcErr != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, rpcErr))
		return
	}
	if rpcResp.Code != resx.Success {
		ctx.JSON(200, resx.GinResp(resx.Fail, nil))
		return
	}

	// 预生成订单
	leftNum, refund, dealList := model.QueryMixedSale(req.GoodId, req.MaxPrice, req.Num, claims.UserId, rpcResp.AccountId)

	// 定时关闭
	for _, dealId := range dealList {
		pubErr := queue.PubItemClose("deal", queue.RkWait1D, dealId)
		if pubErr != nil {
			logx.LogrusObj.Warning("[API] Publish close failed, reason:", pubErr)
		}
	}

	// 对多余部分退款
	if refund != 0 {
		rpcResp2, rpcErr2 := cashServiceClient.CashAdd(ctx, &service.CashChangeRequest{
			Count:  refund,
			UserId: claims.UserId,
		})
		if rpcResp2.GetCode() == 14 {
			discovery.CashDiscover.Sad(cashConn.Target())
		}
		if rpcErr2 != nil {
			ctx.JSON(200, resx.GinResp(resx.SystemErr, rpcErr2))
			return
		}
		if rpcResp2.Code != resx.Success {
			ctx.JSON(200, resx.GinResp(resx.Fail, nil))
			return
		}
	}

	// 预生成后再生成订单，避免多次访问数据库
	var timestamp int64
	switch req.WaitType {
	case queue.RkWait1M:
		timestamp = 60 * 1000
	case queue.RkWait1H:
		timestamp = 60 * 60 * 1000
	case queue.RkWait1D:
		timestamp = 24 * 60 * 60 * 1000
	default:
		req.WaitType = queue.RkWait1M
		timestamp = 60 * 1000
	}

	createState := "ready"
	if leftNum == 0 {
		createState = "closed"
	}

	buy := model.OrderBuy{
		GoodId:   req.GoodId,
		BuyerId:  claims.UserId,
		MaxPrice: req.MaxPrice,
		Num:      req.Num,
		LeftNum:  leftNum,
		Exp:      time.Now().UnixMilli() + timestamp,
		State:    createState,
	}
	err = model.InsertBuy(&buy)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	if leftNum != 0 {
		pubErr := queue.PubItemClose("buy", req.WaitType, buy.BuyId)
		if pubErr != nil {
			logx.LogrusObj.Warning("[API] Publish close failed, reason:", pubErr)
		}
	}

	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

func BuyClose(ctx *gin.Context) {
	jwt := ctx.Request.Header.Get("Authorization")
	claims, err := secret.GetJwtClaims(jwt)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	buyId := ctx.Param("id")

	refund, err := model.UpdateBuyByUserIdAndBuyId(claims.UserId, buyId, "closed")
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.Fail, nil))
		return
	}

	// 退款
	if refund != 0 {
		cashConn := discovery.CashDiscover.Get()
		cashServiceClient := service.NewCashServiceClient(cashConn)
		rpcResp, rpcErr := cashServiceClient.CashAdd(ctx, &service.CashChangeRequest{
			Count:  refund,
			UserId: claims.UserId,
		})
		if rpcResp.GetCode() == 14 {
			discovery.CashDiscover.Sad(cashConn.Target())
		}
		if rpcErr != nil {
			ctx.JSON(200, resx.GinResp(resx.SystemErr, rpcErr))
			return
		}
		if rpcResp.Code != resx.Success {
			ctx.JSON(200, resx.GinResp(resx.Fail, nil))
			return
		}
	}

	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

func DealClose(ctx *gin.Context) {
	jwt := ctx.Request.Header.Get("Authorization")
	claims, err := secret.GetJwtClaims(jwt)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	dealId := ctx.Param("id")

	refund, buyerId, err := model.UpdateDealByUserIdAndDealId(claims.UserId, dealId, "closed")

	// 退款
	if refund != 0 {
		cashConn := discovery.CashDiscover.Get()
		cashServiceClient := service.NewCashServiceClient(cashConn)
		rpcResp, rpcErr := cashServiceClient.CashAdd(ctx, &service.CashChangeRequest{
			Count:  refund,
			UserId: buyerId,
		})
		if rpcResp.GetCode() == 14 {
			discovery.CashDiscover.Sad(cashConn.Target())
		}
		if rpcErr != nil {
			ctx.JSON(200, resx.GinResp(resx.SystemErr, rpcErr))
			return
		}
		if rpcResp.Code != resx.Success {
			ctx.JSON(200, resx.GinResp(resx.Fail, nil))
			return
		}
	}

	ctx.JSON(200, resx.GinResp(resx.Success, nil))
	return
}

type SaleCreateRequest struct {
	GoodId   int64  `json:"goodId"`
	MinPrice int64  `json:"minPrice"`
	Num      int64  `json:"num"`
	WaitType string `json:"waitType"`
}

type BuyCreateRequest struct {
	GoodId   int64  `json:"goodId"`
	MaxPrice int64  `json:"maxPrice"`
	Num      int64  `json:"num"`
	WaitType string `json:"waitType"`
}

func ConfirmLink(ctx *gin.Context) {
	jwt := ctx.Request.Header.Get("Authorization")
	claims, err := secret.GetJwtClaims(jwt)
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	dealId := ctx.Param("id")
	if dealId == "" {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}

	if claims.Audience[0] == "sys" {
		claims.UserId = -1
	}

	res := model.QueryDealBySalerIdOrNot(dealId, claims.UserId)
	if res.State != "ready" {
		ctx.JSON(200, resx.GinResp(resx.NilResult, nil))
		return
	}

	// 生成确认 uuid
	uuid := secret.GenerateUUID()
	seconds := time.Hour
	if err = cache.RedisDB.Set("ctp:deal:confirm:"+uuid, res.DealId, seconds).Err(); err != nil {
		ctx.JSON(200, resx.GinResp(resx.SystemErr, err))
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Success, uuid))
	return
}

func Confirm(ctx *gin.Context) {
	uuid := ctx.Query("uuid")
	if uuid == "" {
		ctx.JSON(200, resx.GinResp(resx.BadRequest, nil))
		return
	}

	dealId, err := cache.RedisDB.Get("ctp:deal:confirm:" + uuid).Result()
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.Fail, nil))
		return
	}

	totalIncome, buyerId, err := model.UpdateDealByDealId(dealId, "finish")
	if err != nil {
		ctx.JSON(200, resx.GinResp(resx.Fail, nil))
		return
	}

	// 交易成功，加钱
	if totalIncome > 0 {
		cashConn := discovery.CashDiscover.Get()
		cashServiceClient := service.NewCashServiceClient(cashConn)
		rpcResp, rpcErr := cashServiceClient.CashAdd(ctx, &service.CashChangeRequest{Count: totalIncome, UserId: buyerId})
		if rpcResp.GetCode() == 14 {
			discovery.CashDiscover.Sad(cashConn.Target())
		}
		if rpcErr != nil {
			ctx.JSON(200, resx.GinResp(resx.SystemErr, rpcErr))
			return
		}
		if rpcResp.Code != resx.Success {
			ctx.JSON(200, resx.GinResp(resx.Fail, nil))
			return
		}

		ctx.JSON(200, resx.GinResp(resx.Success, nil))
		return
	}

	ctx.JSON(200, resx.GinResp(resx.Fail, nil))
	return
}
